/*
 * Galaxium Messenger
 * Copyright (C) 2008 Ben Motmans <ben.motmans@gmail.com>
 *
 * License: GNU General Public License (GPL)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Collections.Generic;

using Gdk;
using Gtk;

using Anculus.Core;
using Anculus.Gui;

using Galaxium.Core;
using Galaxium.Protocol;

namespace Galaxium.Gui.GtkGui
{
	public partial class ContactListView : GalaxiumTreeView
	{
		protected ISession _session;
		protected IConversation _conversation;
		protected IContactTreeManager _manager;
		
		protected Dictionary<IContact, TreeRowReference> _contacts = new Dictionary<IContact, TreeRowReference> ();
		
		protected int _building;
		
		public IContactTreeManager Manager
		{
			get { return _manager; }
			set
			{
				_manager = value;
				_manager.Session = _session;
				_manager.Conversation = _conversation;
				_manager.Init (this);
			}
		}
		
		public ContactListView (IConversation conversation)
			: base (false)
		{
			_session = conversation.Session;
			_conversation = conversation;
			
			_conversation.ContactJoined += ConversationContactJoined;
			_conversation.ContactLeft += ConversationContactLeft;

			_session.ContactChanged += SessionContactChanged;
			_session.ContactPresenceChanged += SessionContactPresenceChanged;
			
			HeadersVisible = false;
			RulesHint = false;
			Reorderable = false;
			
			InitColumns ();
			
			Rebuild ();
		}
		
		public override void Clear ()
		{
			_contacts.Clear ();
			base.Clear();
		}
		
		protected override void OnUnrealized ()
		{
			_conversation.ContactJoined -= ConversationContactJoined;
			_conversation.ContactLeft -= ConversationContactLeft;

			_session.ContactChanged -= SessionContactChanged;
			_session.ContactPresenceChanged -= SessionContactPresenceChanged;
			
			base.OnUnrealized ();
		}
		
		protected virtual void InitColumns ()
		{
			TreeViewColumn col = new TreeViewColumn ();
			col.Sizing = TreeViewColumnSizing.Autosize;
			
			CellRenderer renderer = new CellRendererPixbuf ();
			col.PackStart (renderer, false);
			col.SetCellDataFunc (renderer, new TreeCellDataFunc (RenderLeftImage));
			
			renderer = new CellRendererContact ();
			col.PackStart (renderer, true);
			col.SetCellDataFunc (renderer, new TreeCellDataFunc (RenderText));
			
			renderer = new CellRendererPixbuf ();
			col.PackStart (renderer, false);
			col.SetCellDataFunc (renderer, new TreeCellDataFunc (RenderRightImage));
			
			AppendColumn (col);
		}
		
		public void Rebuild ()
		{
			bool origSort = Sort;
			Sort = false;
			_building++;
			
			Clear ();
			
			foreach (IContact contact in _conversation.ContactCollection)
				AddContact (contact);
			
			_building--;
			Sort = origSort;
		}
		
		protected void AddContact (IContact contact)
		{
			if (!_contacts.ContainsKey (contact))
				_contacts[contact] = AddRow (contact);
			else
				Log.Warn ("Attempted to add an already existing contact to the dictionary: "+contact.DisplayName);
			
			Resort ();
			Refilter ();
		}
		
		protected void RemoveContact (IContact contact)
		{
			if (_contacts.ContainsKey (contact))
			{
				RemoveRow (_contacts[contact]);
				_contacts.Remove (contact);
			}
			else
			{
				Log.Warn ("Attempted to remove a contact which is not present in the dictionary: "+contact.DisplayName);
			}
		}
		
		protected void UpdateContact (IContact contact)
		{
			if (_contacts.ContainsKey (contact))
			{
				RowChanged (_contacts [contact]);
			}
			else
			{
				Log.Warn ("Attempted to update a contact which is not present in the dictionary: "+contact.DisplayName);
			}
		}
		
		void ConversationContactJoined (object sender, ContactActionEventArgs args)
		{
			//Log.Debug ("List View noticed a contact joined: "+args.Contact.DisplayName);
			
			AddContact (args.Contact);
		}
		
		void ConversationContactLeft (object sender, ContactActionEventArgs args)
		{
			//Log.Debug ("List view noticed that a contact left: "+args.Contact.DisplayName);
			RemoveContact (args.Contact);
		}
		
		void SessionContactChanged (object sender, ContactEventArgs args)
		{
			if (_contacts.ContainsKey (args.Contact))
				UpdateContact (args.Contact);
		}
		
		void SessionContactPresenceChanged (object sender, EntityChangeEventArgs<IPresence> args)
		{
			if (_contacts.ContainsKey (args.Entity as IContact))
				UpdateContact (args.Entity as IContact);
		}
		
		void RenderText (TreeViewColumn col, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			_manager.RenderText (GetModelData (iter), cell as CellRendererContact);
		}
		
		void RenderLeftImage (TreeViewColumn col, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			_manager.RenderLeftImage (GetModelData (iter), cell as CellRendererPixbuf);
		}
		
		void RenderRightImage (TreeViewColumn col, CellRenderer cell, TreeModel model, TreeIter iter)
		{
			_manager.RenderRightImage (GetModelData (iter), cell as CellRendererPixbuf);
		}

		protected override int Compare (object obj1, object obj2)
		{
			if (_manager == null)
				return 0;
			
			return _manager.Compare (obj1, obj2);
		}

		protected override bool FilterShouldDisplay (object data)
		{
			if (_manager == null)
				return true;
			
			return _manager.Visible (data, Filter, FilterCaseSensitive);
		}

		protected override InfoTooltip CreateTooltip (TreePath treePath, object data)
		{
			return _manager.GetTooltip (data);
		}
		
		protected override Menu CreateContextMenu (TreePath treePath, object data)
		{
			string menuPath = _manager.GetMenuExtensionPoint (data);
			
			if (string.IsNullOrEmpty (menuPath))
				return null;
			
			if (data is ContactTreeRealGroup)
				data = (data as ContactTreeRealGroup).Group;
			
			ContactListContext context = new ContactListContext (this, treePath, data);
			return MenuUtility.CreateContextMenu (menuPath, new DefaultExtensionContext (context));
		}
	}
}
